package com.mazaiting.instructions.references;

import com.mazaiting.instructions.base.BytecodeReader;
import com.mazaiting.instructions.base.ClassInitLogic;
import com.mazaiting.instructions.base.Index16Instruction;
import com.mazaiting.instructions.base.MethodInvokeLogic;
import com.mazaiting.rtda.MtFrame;
import com.mazaiting.rtda.MtOperandStack;
import com.mazaiting.rtda.heap.*;

import java.util.Objects;

/**
 * 虚拟指令
 *
 * @author mazaiting
 * @since 2021/8/18
 */
public class InvokeInstruction {

    /**
     * Invoke instance method; special handling for superclass, private, and instance initialization method invocations
     * 先拿到当前类、当前常量池、方法符号引用，然后解析符号引用，拿到解析后的类和方法
     * 假定从方法符号引用中解析出来的类是C，方法是M。如果M是构造函数，则声明M的类必须是C，
     * 否则抛出NoSuchMethodError异常。如果M是静态方法，则抛出IncompatibleClassChangeError异常
     */
    public static class InvokeSpecial extends Index16Instruction {
        @Override
        public void execute(MtFrame frame) {
            // 获取当前类
            MtClass currentClass = frame.getMethod().getMtClass();
            // 获取当前常量池
            MtConstantPool pool = currentClass.getConstantPool();
            // 获取方法引用
            MtConstant.MtMethodRef methodRef = (MtConstant.MtMethodRef) pool.getConstant(index);
            // 加载类与方法
            MtClass resolvedClass = methodRef.resolvedClass();
            MtMethod resolvedMethod = methodRef.resolvedMethod();
            // 判断方法名是否为 init
            if (Objects.equals(resolvedMethod.getName(), "<init>") && !Objects.equals(resolvedMethod.getMtClass(), resolvedClass)) {
                throw new RuntimeException("java.lang.NoSuchMethodError");
            }
            // 判断方法是否为静态
            if (resolvedMethod.isStatic()) {
                throw new RuntimeException("java.lang.IncompatibleClassChangeError");
            }
            // 从操作数栈中弹出this引用，如果该引用是null，抛出 NullPointerException异常
            MtObject ref = frame.getOperandStack().getRefFromTop(resolvedMethod.getArgSlotCount() - 1);
            if (Objects.isNull(ref)) {
                throw new RuntimeException("java.lang.NullPointerException");
            }
            // 判断是否为被保护的，是否为子类，包名是否不同， 类实例是否不同
            // 确保protected方法只能被声明该方法的类或子类调用。如果违反这一规定，则抛出IllegalAccessError异常
            if (resolvedMethod.isProtected() && resolvedMethod.getMtClass().isSuperClassOf(currentClass)
                    && !Objects.equals(resolvedMethod.getMtClass().getPackageName(), currentClass.getPackageName())
                    && !Objects.equals(ref.getMtClass(), currentClass)
                    && !ref.getMtClass().isSubClassOf(currentClass)) {
                throw new RuntimeException("java.lang.IllegalAccessError");
            }
            MtMethod methodToBeInvoked = resolvedMethod;
            // 如果调用的中超类中的函数，但不是构造函数，且当前类的 ACC_SUPER标志被设置，需要一个额外的过程查找最终要调用的 方法；
            // 否则前面从方法符号引用中解析出来的方法就是要调用的方法
            if (currentClass.isSuper() && resolvedClass.isSuperClassOf(currentClass)
                    && !Objects.equals(resolvedMethod.getName(), "<init>")) {
                methodToBeInvoked = MtMethodLookup.lookupMethodInClass(currentClass.getSuperClass(), methodRef.getName(), methodRef.getDescriptor());
            }
            // 查找过程失败，或者找到的方法是抽象的，抛出 AbstractMethodError异常
            if (Objects.isNull(methodToBeInvoked) || methodToBeInvoked.isAbstract()) {
                throw new RuntimeException("java.lang.AbstractMethodError");
            }
            // 调用方法
            MethodInvokeLogic.invokeMethod(frame, methodToBeInvoked);
        }

        @Override
        public String toString() {
            return "invokespecial " + index;
        }
    }

    public static class InvokeVirtual extends Index16Instruction {
        @Override
        public void execute(MtFrame frame) {
            MtClass currentClass = frame.getMethod().getMtClass();
            MtConstantPool pool = currentClass.getConstantPool();
            MtConstant.MtMethodRef methodRef = (MtConstant.MtMethodRef) pool.getConstant(index);
            MtMethod resolvedMethod = methodRef.resolvedMethod();
            // 判断方法是否为静态
            if (resolvedMethod.isStatic()) {
                throw new RuntimeException("java.lang.IncompatibleClassChangeError");
            }
            // 获取栈顶引用
            MtObject ref = frame.getOperandStack().getRefFromTop(resolvedMethod.getArgSlotCount() - 1);
            if (Objects.isNull(ref)) {
                // hack
                if (Objects.equals(methodRef.getName(), "println")) {
                    println(frame.getOperandStack(), methodRef.getDescriptor());
                    return;
                }
                throw new RuntimeException("java.lang.NullPointerException");
            }
            if (resolvedMethod.isProtected() && resolvedMethod.getMtClass().isSuperClassOf(currentClass)
                    && Objects.equals(resolvedMethod.getMtClass().getPackageName(), currentClass.getPackageName())
                    && !Objects.equals(ref.getMtClass(), currentClass)
                    && !ref.getMtClass().isSubClassOf(currentClass)) {
                throw new RuntimeException("java.lang.IllegalAccessError");
            }
            MtMethod methodToBeInvoked = MtMethodLookup.lookupMethodInClass(ref.getMtClass(), methodRef.getName(), methodRef.getDescriptor());
            // 查找过程失败，或者找到的方法是抽象的，抛出 AbstractMethodError异常
            if (Objects.isNull(methodToBeInvoked) || methodToBeInvoked.isAbstract()) {
                throw new RuntimeException("java.lang.AbstractMethodError");
            }
            // 调用方法
            MethodInvokeLogic.invokeMethod(frame, methodToBeInvoked);
        }

        /**
         * 打印方法
         *
         * @param stack      操作数栈
         * @param descriptor 描述符
         */
        public void println(MtOperandStack stack, String descriptor) {
            switch (descriptor) {
                case "(Z)V":
                    System.out.println(stack.popInt() != 0);
                    break;
                case "(C)V":
                    System.out.println((char) stack.popInt());
                    break;
                case "(I)V":
                case "(B)V":
                case "(S)V":
                    System.out.println(stack.popInt());
                    break;
                case "(F)V":
                    System.out.println(stack.popFloat());
                    break;
                case "(J)V":
                    System.out.println(stack.popLong());
                    break;
                case "(D)V":
                    System.out.println(stack.popDouble());
                    break;
                default:
                    System.out.println("println: " + descriptor);
                    break;
            }
            stack.popRef();
        }

        @Override
        public String toString() {
            return "invokevirtual " + index;
        }
    }

    /**
     * 调用静态方法
     * 假定解析符号引用后得到方法M。M必须是静态方法，否则抛出Incompatible-ClassChangeError异常。
     * M不能是类初始化方法。类初始化方法只能由Java虚拟机调用，不能使用invokestatic指令调 用
     */
    public static class InvokeStatic extends Index16Instruction {
        @Override
        public void execute(MtFrame frame) {
            // 获取常量池
            MtConstantPool pool = frame.getMethod().getMtClass().getConstantPool();
            // 获取方法引用
            MtConstant.MtMethodRef methodRef = (MtConstant.MtMethodRef) pool.getConstant(index);
            // 解析方法
            MtMethod method = methodRef.resolvedMethod();
            // 如果不是静态的, 则抛出异常
            if (!method.isStatic()) {
                throw new RuntimeException("java.lang.IncompatibleClassChangeError");
            }

            MtClass mtClass = method.getMtClass();
            // 判断类是否已经初始化
            if (!mtClass.isInitStarted()) {
                frame.revertNextPc();
                ClassInitLogic.initClass(frame.getThread(), mtClass);
                return;
            }
            // 调用方法
            MethodInvokeLogic.invokeMethod(frame, method);
        }

        @Override
        public String toString() {
            return "invokestatic " + index;
        }
    }

    public static class InvokeInterface extends Index16Instruction {

        @Override
        public void fetchOperands(BytecodeReader reader) {
            this.index = reader.readU2ToUnsignedShort();
            // count 给方法传递参数需要的slot数，其含义和给Method结构体定义的argSlotCount字段相同
            reader.readU1();
            // must be 0 扩展
            reader.readU1();
        }

        @Override
        public void execute(MtFrame frame) {
            // 先从运行时常量池中拿到并解析接口方法符号引用
            MtConstantPool pool = frame.getMethod().getMtClass().getConstantPool();
            MtConstant.MtInterfaceMethodRef interfaceMethodRef = (MtConstant.MtInterfaceMethodRef) pool.getConstant(index);
            MtMethod resolvedMethod = interfaceMethodRef.resolvedInterfaceMethod();
            // 如果解析后的方法是静态方法或私有方法，则抛出 IncompatibleClassChangeError异常
            if (resolvedMethod.isStatic() || resolvedMethod.isPrivate()) {
                throw new RuntimeException("java.lang.IncompatibleClassChangeError");
            }
            // 从操作数栈中弹出this引用，如果引用是null，则抛出NullPointerException异常。
            // 如果引用所指对象的类没有实现解析出来的接口，则抛出IncompatibleClassChangeError异常
            MtObject ref = frame.getOperandStack().getRefFromTop(resolvedMethod.getArgSlotCount() - 1);
            if (Objects.isNull(ref)) {
                throw new RuntimeException("java.lang.NullPointerException");
            }
            if (!ref.getMtClass().isImplements(interfaceMethodRef.resolvedClass())) {
                throw new RuntimeException("java.lang.IncompatibleClassChangeError");
            }
            // 如果找不到，或者找到的方法是抽象的，则抛出Abstract-MethodError异常。
            // 如果找到的方法不是public， 则抛出IllegalAccessError异常
            MtMethod methodToBeInvoked = MtMethodLookup.lookupMethodInClass(ref.getMtClass(), interfaceMethodRef.getName(), interfaceMethodRef.getDescriptor());
            if (Objects.isNull(methodToBeInvoked) || methodToBeInvoked.isAbstract()) {
                throw new RuntimeException("java.lang.AbstractMethodError");
            }
            if (!methodToBeInvoked.isPublic()) {
                throw new RuntimeException("java.lang.IllegalAccessError");
            }
            // 调用方法
            MethodInvokeLogic.invokeMethod(frame, methodToBeInvoked);
        }

        @Override
        public String toString() {
            return "invokeinterface " + index;
        }
    }
}


















